﻿using System;
using System.Collections.Generic;
using System.Linq;
using BusinessBingo.CommandHandlers.Base;
using BusinessBingo.CommandHandlers.Interfaces;
using BusinessBingo.CommandHandlers.Results;
using BusinessBingo.Commands;
using BusinessBingo.Data.DataLoadOptions;
using BusinessBingo.Data.DataOrderOptions;
using BusinessBingo.Data.Filter;
using BusinessBingo.Data.Interfaces;
using BusinessBingo.Model;
using BusinessBingo.Model.Interfaces;
using BusinessBingo.Queries;
using BusinessBingo.QueryHandlers.Interfaces;
using BusinessBingo.QueryHandlers.Results;

namespace BusinessBingo.CommandHandlers
{
    public class StartNewGameCommandHandler :
        BaseCommandHandler,
        IStartNewGameCommandHandler
    {
        private ICreateNewGameCommandHandler _createNewGameCommandHandler;
        private IGetRandomBingoSetQueryHandler _getRandomBingoSetQueryHandler;
        private ICreateBingoSetCommandHandler _createBingoSetCommandHandler;
        private IGetGroupQueryHandler _getGroupQueryHandler;
        private IGameRepository _gameRepository;
        private IBingoSetRepository _bingoSetRepository;

        public StartNewGameCommandHandler(
            ICreateNewGameCommandHandler createNewGameCommandHandler,
            IGetRandomBingoSetQueryHandler getRandomBingoSetQueryHandler,
            ICreateBingoSetCommandHandler createBingoSetCommandHandler,
            IGetGroupQueryHandler getGroupQueryHandler,
            IGameRepository gameRepository,
            IBingoSetRepository bingoSetRepository,
            IValidationState validationState,
            IUnitOfWork unitOfWork)
            : base(validationState, unitOfWork)
        {
            this._createNewGameCommandHandler = createNewGameCommandHandler;
            this._getRandomBingoSetQueryHandler = getRandomBingoSetQueryHandler;
            this._createBingoSetCommandHandler = createBingoSetCommandHandler;
            this._getGroupQueryHandler = getGroupQueryHandler;
            this._gameRepository = gameRepository;
            this._bingoSetRepository = bingoSetRepository;
        }

        public StartNewGameCommandHandlerResult Execute(StartNewGameCommand startNewGameCommand)
        {
            StartNewGameCommandHandlerResult result = new StartNewGameCommandHandlerResult();

            if(startNewGameCommand == default(StartNewGameCommand))
                throw new ArgumentNullException("StartNewgameCommand");
            if(startNewGameCommand.OldGameId == default(Guid))
                this._validationState.AddError<StartNewGameCommand>(x => x.OldGameId, ErrorTypes.Empty);
            if (startNewGameCommand.BingoSetSize == 0)
                this._validationState.AddError<StartNewGameCommand>(x => x.BingoSetSize, ErrorTypes.Invalid);

            if (this._validationState.IsValid == false)
                return result;

            GameFilter oldGameFilter = new GameFilter();
            oldGameFilter.WithId = startNewGameCommand.OldGameId;

            GameDataLoadOptions dlo = new GameDataLoadOptions();
            dlo.LoadWithGroups = true;

            Game oldGame = this._gameRepository.GetItems(dlo, null, oldGameFilter).Single();

            CreateNewGameCommand createNewGameCommand = new CreateNewGameCommand(oldGame.GroupId);
            CreateNewGameCommandHandlerResult createNewGameCommandHandlerResult =
                this._createNewGameCommandHandler.Execute(createNewGameCommand);
            
            BingoSetFilter bingoSetFilter = new BingoSetFilter();
            bingoSetFilter.WithGameId = oldGame.Id;

            IList<BingoSet> bingoSets = this._bingoSetRepository.GetItems(null, null, bingoSetFilter);

            foreach(BingoSet bingoSet in bingoSets)
            {
                GetRandomBingoSetQuery getRandomBingoSetQuery = new GetRandomBingoSetQuery(startNewGameCommand.BingoSetSize,
                                                                                           startNewGameCommand.Language);
                GetRandomBingoSetQueryHandlerResult getRandomBingoSetQueryHandlerResult =
                    this._getRandomBingoSetQueryHandler.Execute(getRandomBingoSetQuery);

                CreateBingoSetCommand createBingoSetCommand =
                    new CreateBingoSetCommand(getRandomBingoSetQueryHandlerResult.BingoSet,
                                              createNewGameCommandHandlerResult.Game.Id, bingoSet.UserId);
                CreateBingoSetCommandHandlerResult createBingoSetCommandHandlerResult =
                    this._createBingoSetCommandHandler.Execute(createBingoSetCommand);
            }

            GetGroupQuery getGroupQuery = new GetGroupQuery(oldGame.GroupId);
            GetGroupQueryHandlerResult getGroupQueryHandlerResult = this._getGroupQueryHandler.Execute(getGroupQuery);
            result.GroupUrl = getGroupQueryHandlerResult.Group.Url;

            base.Complete();

            result.Game = createNewGameCommandHandlerResult.Game;
            result.Succeeded = true;
            return result;
        }
    }
}